package lt.mii.optimum.components;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import lt.mii.optimum.Start;
import lt.mii.optimum.io.XmlFileOut;

/**
 * Class Represents one Flow (Simultaneous Lessons)<br>
 * Contains List of lessons
 * 
 * @author dstrukov
 * 
 */
public class Flow implements Comparable<Flow> {

	/**
	 * Maximum lessons per flow. Restricted by amount of class rooms
	 */
	static int MAX_LESSONS_PER_FLOW = 30;

	private Day $day;

	private Long $id;

	private List<Lesson> $lessons;

	private Set<Person> $personList;



	public Flow(Day day) {
		$day = day;
		$id = new Long($day.getFlows().size() + 1);

		init();
	}



	/**
	 * Adds a single lesson to the Flow
	 * 
	 * @param lesson
	 *            {@link Lesson} to add
	 * @return Returns <code>false</code> if failed.
	 */
	boolean addLesson(Lesson lesson) {

		Iterator<Student> studIter = lesson.getStudents().iterator();

		while (studIter.hasNext()) {
			addStudent(studIter.next());
		}

		addTeacher(lesson.getTeacher());

		// lesson.setFlow(this);

		$lessons.add(lesson);

		return true;
	}



	/**
	 * Adds a {@link Student} to a Flow <br>
	 * One student can belong to one Flow.
	 * 
	 * @param student
	 *            Student to add
	 * @return Returns false if student is already in the flow
	 */
	boolean addStudent(Student student) {
		assert student != null;
		return $personList.add(student);
	}



	/**
	 * Adds a {@link Teacher} to a Flow <br>
	 * One teacher can belong to one Flow.
	 * 
	 * @param teacher
	 *            Teacher to add
	 * @return Returns false if teacher is already in the flow
	 */

	boolean addTeacher(Teacher teacher) {
		assert teacher != null;
		return $personList.add(teacher);
	}



	/**
	 * Attempts to compile the Flow.
	 * 
	 * @return Returns <code>false</code> if no {@link Lesson}s were added
	 * @param priority
	 *            Subjects of which priority level to compile
	 */
	public boolean compile(int priority) {
		boolean flag = false;

		Iterator<Subject> it = Start.data.getSubjects().iterator();

		while (it.hasNext()) {
			Subject subject = it.next();

			if (subject.getPriority() != priority)
				continue;

			while ($lessons.size() <= MAX_LESSONS_PER_FLOW) {

				Lesson lesson = new Lesson(this);
				if (!lesson.compile(subject)) {
					// System.out.println(lesson.getCompilationMessage());

					break;
				}
				addLesson(lesson);
				flag = true;

			}
		}

		if (flag) {
			Collections.sort($lessons);
		}

		return flag;

	}



	public List<Lesson> getLessons() {
		return $lessons;
	}



	public Set<Person> getPersonList() {
		return $personList;
	}



	/**
	 * Initializes Flow
	 */
	private void init() {
		$lessons = new ArrayList<Lesson>();
		$personList = new HashSet<Person>();

	}



	/**
	 * Prints content of the flow to given output
	 * 
	 * @param out -
	 *            {@link XmlFileOut} desired xml file output
	 */
	public void output(XmlFileOut out) {

		Iterator<Lesson> it = $lessons.iterator();
		while (it.hasNext()) {
			out.addTag(it.next().getSubject().getSubjCode());
		}

	}



	/**
	 * Removes recursively lesson from the flow.<br>
	 * (Also removes teacher and students from the flow)
	 * 
	 * 
	 * @param lesson
	 *            to remove
	 * @return returns <code>true</code>
	 */
	boolean removeLesson(Lesson lesson) {

		Iterator<Student> studIter = lesson.getStudents().iterator();

		while (studIter.hasNext()) {
			removeStudent(studIter.next());
		}

		removeTeacher(lesson.getTeacher());

		// lesson.setFlow(null);

		return true;
	}



	/**
	 * Remove Student from the flow
	 * 
	 * @param student
	 *            to remove
	 * @return true if this set contained the specified student
	 */
	public boolean removeStudent(Student student) {
		return $personList.remove(student);
	}



	/**
	 * Remove Teacher from the flow
	 * 
	 * @param teacher
	 *            to remove
	 * @return true if this set contained the specified teacher
	 */
	boolean removeTeacher(Teacher teacher) {
		return $personList.remove(teacher);
	}



	@Override
	public String toString() {
		Iterator<Lesson> it = $lessons.iterator();
		String returnStr = $id.toString();
		while (it.hasNext()) {
			returnStr += " " + it.next().toString() + ",";
		}
		return returnStr;
	}



	public Long getId() {
		return $id;
	}



	@Override
	public int compareTo(Flow o) {
		if (this.$id == o.getId())
			return 0;
		if (this.$id > o.getId())
			return 1;
		assert this.$id < o.getId();

		return -1;
	}

}
